﻿using Newtonsoft.Json.Converters;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Data;
using System.Dynamic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Security.Cryptography;
using System.Text;

public static class Ext
{
    /// <summary>
    /// 获取最初导致异常的Message
    /// </summary>
    /// <param name="ex">异常对象</param>
    /// <returns></returns>
    public static string RawMsg(this Exception ex)
    {
        while (ex.InnerException != null)
        {
            ex = ex.InnerException;
        }
        return ex.Message;
    }

    /// <summary>
    /// 在给定所有Keys 搜索获取value
    /// </summary>
    public static TValue GetValue<TValue>(this IDictionary<string, string> @this, TValue defaultValue, params string[] keys)
    {
        foreach (var key in keys)
        {
            if (@this.ContainsKey(key))
                return (TValue)Convert.ChangeType(@this[key], typeof(TValue));
        }
        return defaultValue;
    }

    public static string ToJsonString(this object @object)
    {
        List<Newtonsoft.Json.JsonConverter> lstJCvtr = new List<Newtonsoft.Json.JsonConverter>();

        //序列化 对象成Json字符串
        Newtonsoft.Json.Formatting format = Newtonsoft.Json.Formatting.None;
        //时间格式化
        IsoDateTimeConverter dtConverter = new IsoDateTimeConverter { DateTimeFormat = "yyyy-MM-dd HH:mm:ss:fff" };
        //枚举类型格式化
        StringEnumConverter emConverter = new StringEnumConverter();

        lstJCvtr.Add(dtConverter);
        lstJCvtr.Add(emConverter);
        return Newtonsoft.Json.JsonConvert.SerializeObject(@object, format, lstJCvtr.ToArray());
    }

    public static T ToObject<T>(this string json)
    {
        List<Newtonsoft.Json.JsonConverter> lstJCvtr = new List<Newtonsoft.Json.JsonConverter>();

        //时间格式化
        IsoDateTimeConverter dtConverter = new IsoDateTimeConverter { DateTimeFormat = "yyyy-MM-dd HH:mm:ss:fff" };
        //枚举类型格式化
        StringEnumConverter emConverter = new StringEnumConverter();

        lstJCvtr.Add(dtConverter);
        lstJCvtr.Add(emConverter);

        return Newtonsoft.Json.JsonConvert.DeserializeObject<T>(json, lstJCvtr.ToArray());
    }


    /// <summary>
    /// 引用类型对象的序列化（深度克隆）
    /// 注：使用之前先将对象标记为  [Serializable] 可序列化。
    /// </summary>
    /// <typeparam name="T">对象类型</typeparam>
    /// <param name="RealObject"></param>
    /// <returns></returns>
    public static T Clone<T>(this T RealObject)
    {
        using (Stream objectStream = new MemoryStream())
        {
            //利用 System.Runtime.Serialization序列化与反序列化完成引用对象的复制  
            IFormatter formatter = new BinaryFormatter();
            formatter.Serialize(objectStream, RealObject);
            objectStream.Seek(0, SeekOrigin.Begin);
            return (T)formatter.Deserialize(objectStream);
        }
    }

    /// <summary>
    /// 将DataRow转为 IDictionary 类型数据
    /// </summary>
    /// <param name="this"></param>
    /// <returns></returns>
    public static Dictionary<string, object> ToDictionary(this DataRow @this)
    {
        var dict = new Dictionary<string, object>();
        foreach (DataColumn column in @this.Table.Columns)
        {
            dict.Add(column.ColumnName, @this[column]);
        }
        return dict;
    }


    


    #region NameValueCollection 扩展

    /// <summary>
    /// 返回可写 NameValueCollection集合
    /// </summary>
    public static NameValueCollection NoReadonly(this NameValueCollection @this)
    {
        if (@this == null)
        {
            return new NameValueCollection();
        }
        return new NameValueCollection(@this);
    }

    /// <summary>
    /// 是否包含某个键
    /// </summary>
    /// <param name="keyName">键名</param>
    /// <param name="valueIsSpace">value值 是否 允许 为空字符串</param>
    public static bool ContainsKey(this NameValueCollection @this, string keyName, bool valueIsSpace = false)
    {
        if (@this == null || @this.Count <= 0)
        {
            return false;
        }

        string[] values = @this.GetValues(keyName);

        if (values == null)
        {
            return false;
        }

        if (valueIsSpace)
        {
            return true;
        }

        int ct = 0;
        foreach (var val in values)
        {
            if (string.IsNullOrWhiteSpace(val))
            {
                ct++;
            }
        }
        return !(ct == values.Length);
    }

    /// <summary>
    /// 是否包含某个值
    /// </summary>
    /// <param name="value">值</param>
    /// <param name="ignoreCase">是否忽略大小写</param>
    public static bool ContainsValue(this NameValueCollection @this, string value, bool ignoreCase = true)
    {
        if (@this == null || @this.Count <= 0)
        {
            return false;
        }

        StringComparison strCmp = ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.CurrentCulture;
        foreach (var keyName in @this.AllKeys)
        {
            if (@this.Get(keyName).Equals(value, strCmp))
            {
                return true;
            }
        }
        return false;
    }


    /// <summary>
    /// 尝试获取 某个键的值
    /// </summary>
    /// <param name="keyName">键名</param>
    /// <param name="values">values值数组</param>
    public static bool TryGetValues(this NameValueCollection @this, string keyName, out string[] values)
    {
        values = null;
        return TryGetValues(@this, keyName, false, out values);
    }

    /// <summary>
    /// 尝试获取 某个键的值
    /// </summary>
    /// <param name="keyName">键名</param>
    /// <param name="valueIsSpace">value值 是否 允许 为空字符串</param>
    /// <param name="values">values值数组</param>
    public static bool TryGetValues(this NameValueCollection @this, string keyName, bool valueIsSpace, out string[] values)
    {
        values = null;

        if (@this == null || @this.Count <= 0)
        {
            return false;
        }

        values = @this.GetValues(keyName);

        if (values == null)
        {
            return false;
        }

        if (valueIsSpace && values.Length > 0)
        {
            return true;
        }
        else
        {
            int ct = 0;
            foreach (var val in values)
            {
                if (string.IsNullOrWhiteSpace(val))
                {
                    ct++;
                }
            }
            return !(ct == values.Length);
        }
    }



    /// <summary>
    /// 尝试获取 某个键的值
    /// </summary>
    /// <param name="keyName">键名</param>
    /// <param name="values">value值数组</param>
    public static bool TryGetValue(this NameValueCollection @this, string keyName, out string value)
    {
        value = null;
        return TryGetValue(@this, keyName, false, out value);
    }


    /// <summary>
    /// 尝试获取 某个键的值
    /// </summary>
    /// <param name="keyName">键名</param>
    /// <param name="valueIsSpace">value值 是否 允许 为空字符串</param>
    /// <param name="values">value值数组</param>
    public static bool TryGetValue(this NameValueCollection @this, string keyName, bool valueIsSpace, out string value)
    {
        value = null;

        if (@this == null || @this.Count <= 0)
        {
            return false;
        }

        value = @this.Get(keyName);

        if (value == null)
        {
            return false;
        }

        if (valueIsSpace && value.Length > 0)
        {
            return true;
        }
        else
        {
            if (string.IsNullOrWhiteSpace(value))
            {
                return false;
            }
            return true;
        }
    }

    public static bool Remove(this NameValueCollection @this, string keyName, string value)
    {
        if (@this == null || @this.Count <= 0)
        {
            return false;
        }

        var lstVals = @this.GetValues(keyName)?.ToList();

        if (lstVals == null)
        {
            return false;
        }

        int index = lstVals.IndexOfCompare(value);

        if (index <= -1)
        {
            return false;
        }

        lstVals.RemoveAt(lstVals.IndexOfCompare(value));
        @this.Remove(keyName);

        for (int j = 0; j < lstVals.Count; j++)
        {
            @this.Add(keyName, lstVals[j]);
        }
        return true;
    }

    public static TValue GetValue<TValue>(this NameValueCollection @this,  TValue defaultValue, params string[] keys)
    {
        if (@this == null || @this.Count <= 0)
        {
            return defaultValue;
        }

        foreach (var key in keys)
        {
            string value;
            if (@this.TryGetValue(key, out value))
                return (TValue)Convert.ChangeType(value, typeof(TValue));
        }
        return defaultValue;

    }

    private static int IndexOfCompare(this IEnumerable<string> @this, string item, StringComparison comparisonType = StringComparison.OrdinalIgnoreCase)
    {
        if (@this == null || !@this.Any())
        {
            return -1;
        }

        int j = -1;
        foreach (string curStr in @this)
        {
            j++;

            if (
                (curStr == null && item == null) ||
                (curStr != null && curStr.Equals(item, comparisonType))
               )

            {
                return j;
            }
        }
        return -1;
    }


    #endregion


    #region 数据类型

    /// <summary>
    /// 对象类型转换
    /// </summary>
    /// <typeparam name="T">返回的数据，数据的类型</typeparam>
    /// <param name="this">当前值</param>
    /// <returns>转换后的对象</returns>
    public static T ChangeType<T>(this object @this)
    {
        object result = null;

        Type toType = typeof(T);

        if (@this == null || @this == DBNull.Value)
        {

            if ((toType.IsGenericType && toType.GetGenericTypeDefinition().Equals(typeof(Nullable<>))))
            {
                return default(T);
            }
            else if (toType.IsValueType)
            {
                throw new Exception("不能将null值转换为" + toType.Name + "类型!");
            }
            else
            {
                return default(T);
            }
        }
        else
        {
            if ((toType.IsGenericType && toType.GetGenericTypeDefinition().Equals(typeof(Nullable<>))))
            {
                toType = Nullable.GetUnderlyingType(toType) ?? toType;
            }

            if (toType.Name == "Object")
            {
                return (T)@this;
            }

            if (toType.IsEnum)
            {
                result = Enum.Parse(toType, @this.ToString(), true);
            }
            else if (toType.IsAssignableFrom(typeof(Guid)))
            {
                result = Guid.Parse(@this.ToString());
            }
            else
            {
                result = Convert.ChangeType(@this, toType);
            }
            return (T)result;
        }

    }

    /// <summary>
    /// 对象类型转换，转换失败，返回默认值
    /// </summary>
    /// <typeparam name="T">返回类型</typeparam>
    /// <param name="this">当前值</param>
    /// <param name="def">转换失败，返回的默认值</param>
    /// <returns>返回转换后的对象或转换失败后的默认值</returns>
    public static T ChangeType<T>(this object @this, T def)
    {
        try
        {
            if (@this == null)
            {
                return def;
            }

            return ChangeType<T>(@this);
        }
        catch
        {
            return def;
        }
    }


    /// <summary>
    /// 对象类型转换
    /// </summary>
    /// <param name="this">当前值</param>
    /// <param name="conversionType">指定类型的类型</param>
    /// <returns>转换后的对象</returns>
    public static object ChangeType(this object @this, Type conversionType)
    {
        Type type = conversionType;

        type = Nullable.GetUnderlyingType(type) ?? type;

        if (type != null)
        {
            object result = null;

            if (@this != null && @this != DBNull.Value)
            {
                if (type.IsAssignableFrom(typeof(string)))
                {
                    result = @this.ToString();
                }
                else if (type.IsEnum)
                {
                    result = Enum.Parse(type, @this.ToString(), true);
                }
                else if (type.IsAssignableFrom(typeof(Guid)))
                {
                    result = Guid.Parse(@this.ToString());
                }
                else
                {
                    result = Convert.ChangeType(@this, type);
                }
            }
            else
            {
                if (type.IsAssignableFrom(typeof(string)) || type.IsAssignableFrom(typeof(object)))
                {
                    result = null;
                }
                else
                {
                    throw new Exception("不能将null值转换为" + type.Name + "类型!");
                }
            }
            return result;
        }
        return Convert.ChangeType(@this, type);
    }


    /// <summary>
    /// 对象类型转换
    /// </summary>
    /// <param name="this">当前值</param>
    /// <param name="conversionType">指定类型的类型</param>
    /// <param name="def">转换失败，返回的默认值</param>
    /// <returns>转换后的对象</returns>
    public static object ChangeType(this object @this, Type conversionType, object def)
    {
        try
        {
            if (@this == null)
            {
                return def;
            }

            return ChangeType(@this, conversionType);
        }
        catch
        {
            return def;
        }
    }

    /// <summary>
    /// 判定 对象的类型 是否是 可空类型
    /// </summary>
    /// <param name="this">对象类型</param>
    /// <returns>是否是 可空类型</returns>
    public static bool isnullableType(this Type @this)
    {
        return (@this.IsGenericType && @this.
          GetGenericTypeDefinition().Equals
          (typeof(Nullable<>)));
    }

    /// <summary>
    /// 返回当前对象的数组形式
    /// </summary>
    /// <typeparam name="T">当前数据类型</typeparam>
    /// <param name="this">当前对象</param>
    /// <returns>当前对象的数组形式</returns>
    public static T[] ConvertArray<T>(this T @this)
    {
        return new T[] { @this };
    }

    /// <summary>
    /// 返回当前对象的列表形式
    /// </summary>
    /// <typeparam name="T">当前数据类型</typeparam>
    /// <param name="this">当前对象</param>
    /// <returns>当前对象的列表形式</returns>
    public static List<T> ConvertList<T>(this T @this)
    {
        List<T> lst = new List<T>();
        lst.Add(@this);
        return lst;
    }

    #endregion

    #region DataTable DataRow DataColumn 扩展

    /// <summary>
    /// 改变:将DataTable的其中 某列(默认第一列)数据 存储为 List
    /// </summary>
    /// <typeparam name="T">数据类型</typeparam>
    /// <param name="this">数据源</param>
    /// <param name="columnName">列名,默认第一列</param>
    /// <returns>List数据</returns>
    public static List<T> MapperList<T>(this DataTable @this, string columnName = null)
    {
        List<T> lst = new List<T>();
        if (@this == null || @this.Rows.Count < 0)
        {
            return lst;
        }
        else
        {
            foreach (DataRow dr in @this.Rows)
            {
                if (columnName == null)
                {
                    lst.Add(dr[0].ChangeType<T>());
                }
                else
                {
                    lst.Add(dr[columnName].ChangeType<T>());
                }
            }
        }
        return lst;
    }


    /// <summary>
    /// 将DataTable的其中 两列数据 存储为 Dictionary
    /// </summary>
    /// <param name="this">数据源</param>
    /// <param name="ColumnNameKey">列1(键)</param>
    /// <param name="ColumnVal">列2(值)</param>
    /// <returns>Dictionary集合</returns>
    public static Dictionary<TKey, TVal> MapperDict<TKey, TVal>(this DataTable @this, string ColumnNameKey, string ColumnVal)
    {
        Dictionary<TKey, TVal> dict = new Dictionary<TKey, TVal>();
        if (@this == null || @this.Rows.Count <= 0)
        {
            return dict;
        }
        else
        {
            foreach (DataRow dr in @this.Rows)
            {
                TKey k = (TKey)dr[ColumnNameKey].ChangeType(typeof(TKey));
                TVal v = (TVal)dr[ColumnVal].ChangeType(typeof(TVal));
                dict.Add(k, v);
            }
        }
        return dict;
    }


    /// <summary>
    /// 将DataTable的其中 两列数据 存储为 NameValueCollection
    /// </summary>
    /// <param name="this">数据源</param>
    /// <param name="ColumnNameKey">列1(键)</param>
    /// <param name="ColumnVal">列2(值)</param>
    /// <returns>NameValueCollection集合</returns>
    public static NameValueCollection MapperNameValues(this DataTable @this, string ColumnNameKey, string ColumnVal)
    {
        NameValueCollection nvc = new NameValueCollection();
        if (@this == null || @this.Rows.Count <= 0)
        {
            return nvc;
        }
        else
        {
            foreach (DataRow dr in @this.Rows)
            {
                nvc.Add(dr[ColumnNameKey].ToString(), dr[ColumnVal].ToString());
            }
        }
        return nvc;
    }


    /// <summary>
    ///给当前DataTable增加列名 
    /// </summary>
    /// <param name="this">DataTable对象</param>
    /// <param name="columns">列信息</param>
    /// <returns>增加列后的DataTable</returns>
    public static DataTable AddColumns(this DataTable @this, params KeyValuePair<string, Type>[] columns)
    {
        if (@this == null)
        {
            @this = new DataTable();
        }
        if (columns != null && columns.Length > 0)
        {
            foreach (var col in columns)
            {
                @this.Columns.Add(new DataColumn(col.Key, col.Value));
            }
        }
        return @this;
    }

    /// <summary>
    /// 获取首行数据
    /// </summary>
    /// <param name="this">DataTable数据</param>
    /// <returns>首行数据</returns>
    public static DataRow FirstRow(this DataTable @this)
    {
        if (@this == null)
        {
            throw new ArgumentNullException("DataTable不能为null");
        }
        if (@this.Rows.Count > 0)
        {
            return @this.Rows[0];
        }
        return null;
    }

    /// <summary>
    /// 获取最后一行数据
    /// </summary>
    /// <param name="this">DataTable数据</param>
    /// <returns>最后一行数据</returns>
    public static DataRow LastRow(this DataTable @this)
    {
        if (@this == null)
        {
            throw new ArgumentNullException("DataTable不能为null");
        }

        if (@this.Rows.Count > 0)
        {
            return @this.Rows[@this.Rows.Count - 1];
        }
        return null;
    }

    /// <summary>
    /// 添加列数据
    /// </summary>
    /// <param name="this">行集合</param>
    /// <param name="drs">行数据</param>
    public static void AddRange(this DataRowCollection @this, IEnumerable<DataRow> drs)
    {
        foreach (DataRow dr in drs)
        {
            @this.Add(dr.ItemArray);
        }
    }

    /// <summary>
    /// 获取所有DataColumn
    /// </summary>
    /// <param name="columnCollection">列集合</param>
    /// <returns>列数组</returns>
    public static DataColumn[] ToArray(this DataColumnCollection columnCollection)
    {
        List<DataColumn> lstDC = new List<DataColumn>();
        foreach (DataColumn dc in columnCollection)
        {
            lstDC.Add(dc);
        }
        return lstDC.ToArray();
    }



    /// <summary>
    /// 将DataRow转为 dynamic 类型对象
    /// </summary>
    /// <param name="this">行数据</param>
    /// <returns></returns>
    public static dynamic ToExpandoObject(this DataRow @this)
    {
        dynamic entity = new ExpandoObject();
        var expandoDict = (IDictionary<string, object>)entity;
        foreach (DataColumn column in @this.Table.Columns)
        {
            expandoDict.Add(column.ColumnName, @this[column]);
        }
        return expandoDict;
    }

    #endregion

    public static List<string> GetListEnums(this Enum em, bool underlyingInt = false)
    {
        List<string> lstEnums = new List<string>();
        Type tyEnum = em.GetType();
        foreach (string str in tyEnum.GetEnumNames())
        {
            string item = str;
            if (underlyingInt)
            {
                item = Convert.ToInt32(Enum.Parse(tyEnum, str)).ToString();
            }
            lstEnums.Add(item);
        }
        return lstEnums;
    }
}